
/***************************************************************************
 *   fecab 
 *                                                                         *
 *   Copyright (C) 2010 by kuerant                                         *
 *                         <kuerant@gmail.com>                             *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Library General Public License as       *
 *   published by the Free Software Foundation; either version 2 of the    *
 *   License, or (at your option) any later version.                       *
 *                                                                         *
 *   This library is distributed in the hope that it will be usefull, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      *
 *   Library General Public License for more details.                      *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 59 Temple Place - Suite 330, Boston,                *
 *   MA 02111-1307, USA                                                    *
 ***************************************************************************/


#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>

#include <boost/filesystem.hpp>

#include <boost/program_options.hpp>
namespace po = boost::program_options;

#include <boost/date_time/posix_time/posix_time.hpp>

#include <pthread.h>
#include <signal.h>

#include "bocat/common/logging.hpp"

#include "server.hpp"
#include "store_service.hpp"

static  bocat::common::Logger*  logger_p = NULL;

#define INFO(ARGS...)   logger_p->info(__LINE__, __FILE__, ARGS)
#define ERROR(ARGS...)  logger_p->error(__LINE__, __FILE__, ARGS)



int main(int argc, char* argv[])
{
    std::string ip;
    std::string port;
	std::string	store_config;
    int thread_count = 0;

	std::string	log_config;
	std::string	log_section;


    try {
        po::options_description desc("Allowed options");
        desc.add_options()
            ("help,h", "produce help message")
            ("ip", po::value<std::string>(&ip)->default_value("0.0.0.0"), "server ip/hostname")
            ("port", po::value<std::string>(&port)->default_value("4380"), "server port")
            ("store-conf", po::value<std::string>(&store_config)->default_value("store.conf"), "storage config file")
            ("threads", po::value<int>(&thread_count)->default_value(1), "number of threads to run")

            ("log-config", po::value<std::string>(&log_config), "path to logging config file")
            ("log-section", po::value<std::string>(&log_section), "logging section name in config file")
        ;

        po::variables_map vm;
        po::store(po::parse_command_line(argc, argv, desc), vm);
        po::notify(vm);

        if (vm.count("help")) {
            std::cout << desc << "\n";
            return 1;
        }

        if ((vm.count("log-config") > 0) && (vm.count("log-section") < 1)) {
            std::cout << "\nlog-section must be provided\n\n";
            std::cout << desc << "\n";
            return 2;
        }

    }
    catch(std::exception& e) {
        std::cerr << "error: " << e.what() << "\n";
        return 1;
    }
    catch(...) {
        std::cerr << "Exception of unknown type!\n";
        return 2;
    }


    boost::filesystem::path store_conf_path( store_config );
    if ( !boost::filesystem::exists( store_conf_path ) ) { 
        std::cerr << "config file " << store_config << " does NOT exist.\n";
        return 3;
    }   
    if ( !boost::filesystem::is_regular_file( store_conf_path ) ) { 
        std::cerr << "config file " << store_config << " is NOT a regular file.\n";
        return 4;
    }   

    logger_p = bocat::common::getLogger();
    if ( log_config.size() > 0 ) {
        logger_p->configure( log_config.c_str(), log_section.c_str() );
    }


	boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();

	try
	{
		// Block all signals for background thread.
		sigset_t new_mask;
		sigfillset(&new_mask);
		sigset_t old_mask;
		pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask);

		//now = boost::posix_time::second_clock::local_time();
		//std::cout << boost::posix_time::to_iso_extended_string(now) << " * initializing store ...\n";
		//std::cout << now << " * initializing store ...\n";
		INFO(" * initializing store ...");
		// initialized Store Service
		fecab::server::StoreService	store_service( store_config );

		//now = boost::posix_time::second_clock::local_time();
		//std::cout << now << " * listening on " << ip << ":" << port << " ...\n";
		INFO(" * listening on %s:%s ...", ip.c_str(), port.c_str());
		// Run server in background thread.
		////std::size_t num_threads = boost::lexical_cast<std::size_t>(argv[3]);
		fecab::server::server s(ip, port, thread_count, &store_service);
		boost::thread t(boost::bind(&fecab::server::server::run, &s));

		// Restore previous signals.
		pthread_sigmask(SIG_SETMASK, &old_mask, 0);

		// Wait for signal indicating time to shut down.
		sigset_t wait_mask;
		sigemptyset(&wait_mask);
		sigaddset(&wait_mask, SIGINT);
		sigaddset(&wait_mask, SIGQUIT);
		sigaddset(&wait_mask, SIGTERM);
		pthread_sigmask(SIG_BLOCK, &wait_mask, 0);
		int sig = 0;
		sigwait(&wait_mask, &sig);

		// Stop the server.
		s.stop();
		t.join();

		//now = boost::posix_time::second_clock::local_time();
		//std::cout << now << "   shutdown ...\n";
		INFO("    shutdown ...");
	}
	catch (std::exception& e)
	{
		//std::cerr << "exception: " << e.what() << "\n";
		ERROR("exception: %s", e.what());
	}

	//now = boost::posix_time::second_clock::local_time();
	//std::cout << now << " done.\n";
	INFO("    done.");

	return 0;
}


